#! /usr/bin/env python3

import sys
import time
import random
from optparse import OptionParser

from common.zerror import zassert


#
# HELPER
#
def dospace(howmuch):
    for i in range(howmuch):
        print(('%24s' % ' '), end=' ')


class CPU:
    #
    # INIT: how much memory?
    #
    def __init__(self, memory, memtrace, regtrace, cctrace, compute, verbose):
        # conditions
        self.COND_GT = 0
        self.COND_GTE = 1
        self.COND_LT = 2
        self.COND_LTE = 3
        self.COND_EQ = 4
        self.COND_NEQ = 5
        self.cond_list = [self.COND_GTE, self.COND_GT, self.COND_LTE, self.COND_LT, self.COND_NEQ, self.COND_EQ]
        # registers in system
        self.REG_ZERO = 0
        self.REG_AX = 1
        self.REG_BX = 2
        self.REG_CX = 3
        self.REG_DX = 4
        self.REG_SP = 5
        self.REG_BP = 6
        self.reg_list = [self.REG_ZERO, self.REG_AX, self.REG_BX, self.REG_CX, self.REG_DX, self.REG_SP, self.REG_BP]
        self.reg_name_map = {'zero': self.REG_ZERO, 'ax': self.REG_AX, 'bx': self.REG_BX, 'cx': self.REG_CX,
                             'dx': self.REG_DX, 'sp': self.REG_SP, 'bp': self.REG_BP}
        # system memory: in KB
        self.max_memory = memory * 1024
        # which memory addrs and registers to trace?
        self.memtrace = memtrace
        assert isinstance(self.memtrace, list)
        self.regtrace = regtrace
        assert isinstance(self.regtrace, list)
        self.cctrace = cctrace
        self.compute = compute
        self.verbose = verbose

        self.PC = 0
        self.registers = {}
        self.conditions = {}
        self.labels = {}
        self.vars = {}
        self.memory = {}  # 内存: 字典的键是地址, value是内存空间存放的值
        self.pmemory = {}  # 程序内存: 存放代码的内存

        tmplist = []  # 将不符合要求的寄存器剔除?
        for r in self.regtrace:
            zassert(r in self.reg_name_map, f'寄存器 {r} 不存在, 不可被追踪')
            tmplist.append(self.reg_name_map[r])
        self.regtrace = tmplist

        self.init_memory()
        self.init_registers()
        self.init_condition_codes()

    #
    # 模拟器运行前的初始化
    #
    def init_memory(self):
        """提前将所有内存空间分配好了"""
        for i in range(self.max_memory):
            self.memory[i] = 0

    def init_registers(self):
        for i in self.reg_list:
            self.registers[i] = 0

    def init_condition_codes(self):
        for c in self.cond_list:
            self.conditions[c] = False

    def dump_memory(self):
        """应该是测试用的, 没有被调用过, 用于输出除了程序以外的内存中的数据"""
        print('MEMORY DUMP')
        for i in range(self.max_memory):
            if i not in self.pmemory and i in self.memory and self.memory[i] != 0:
                print('  m[%d]' % i, self.memory[i])

    #
    # INFORMING ABOUT THE HARDWARE
    #
    def get_regnum(self, name):
        """通过名称查询寄存器号"""
        assert (name in self.reg_name_map)
        return self.reg_name_map[name]

    def get_regname(self, num):
        """通过寄存器号查询名称, 没查到返回空字符串"""
        assert (num in self.reg_list)
        for rname in self.reg_name_map:
            if self.reg_name_map[rname] == num:
                return rname
        return ''

    def get_reglist(self) -> list:
        return self.reg_list

    def get_condlist(self) -> list:
        return self.cond_list

    def get_reg(self, reg):
        """通过寄存器号查询寄存器中存放的值"""
        assert (reg in self.reg_list)
        return self.registers[reg]

    def get_cond(self, cond):
        """通过条件号查询条件的状态"""
        assert (cond in self.cond_list)
        return self.conditions[cond]

    def get_pc(self):
        return self.PC

    def set_reg(self, reg, value):
        assert (reg in self.reg_list)
        self.registers[reg] = value

    def set_cond(self, cond, value):
        assert (cond in self.cond_list)
        self.conditions[cond] = value

    def set_pc(self, pc):
        self.PC = pc

    #
    # 指令
    #
    def nop(self):
        return 0

    def halt(self):
        return -1

    def iyield(self):
        return -2

    def rdump(self):
        print('REGISTERS::', end=' ')
        print('ax:', self.registers[self.REG_AX], end=' ')
        print('bx:', self.registers[self.REG_BX], end=' ')
        print('cx:', self.registers[self.REG_CX], end=' ')
        print('dx:', self.registers[self.REG_DX], end=' ')

    def mdump(self, index):
        print('m[%d] ' % index, self.memory[index])

    def move_i_to_r(self, src, dst):
        self.registers[dst] = src
        return 0

    # memory: value, register, register
    def move_i_to_m(self, src, value, reg1, reg2):
        tmp = value + self.registers[reg1] + self.registers[reg2]
        self.memory[tmp] = src
        return 0

    def move_m_to_r(self, value, reg1, reg2, dst):
        tmp = value + self.registers[reg1] + self.registers[reg2]
        # print 'doing mov', 'val:', value, 'r1:', self.get_regname(reg1), self.registers[reg1], 'r2:', self.get_regname(reg2), self.registers[reg2], 'dst', self.get_regname(dst), 'tmp', tmp, 'reg[dst]', self.registers[dst], 'mem', self.memory[tmp]
        self.registers[dst] = self.memory[tmp]

    def move_r_to_m(self, src, value, reg1, reg2):
        tmp = value + self.registers[reg1] + self.registers[reg2]
        self.memory[tmp] = self.registers[src]
        return 0

    def move_r_to_r(self, src, dst):
        self.registers[dst] = self.registers[src]
        return 0

    def add_i_to_r(self, src, dst):
        self.registers[dst] += src
        return 0

    def add_r_to_r(self, src, dst):
        self.registers[dst] += self.registers[src]
        return 0

    def sub_i_to_r(self, src, dst):
        self.registers[dst] -= src
        return 0

    def sub_r_to_r(self, src, dst):
        self.registers[dst] -= self.registers[src]
        return 0

    #
    # SUPPORT FOR LOCKS
    #
    def atomic_exchange(self, src, value, reg1, reg2):
        tmp = value + self.registers[reg1] + self.registers[reg2]
        old = self.memory[tmp]
        self.memory[tmp] = self.registers[src]
        self.registers[src] = old
        return 0

    def fetchadd(self, src, value, reg1, reg2):
        tmp = value + self.registers[reg1] + self.registers[reg2]
        old = self.memory[tmp]
        self.memory[tmp] = self.memory[tmp] + self.registers[src]
        self.registers[src] = old

    #
    # TEST for conditions
    #
    def test_all(self, src, dst):
        self.init_condition_codes()
        if dst > src:
            self.conditions[self.COND_GT] = True
        if dst >= src:
            self.conditions[self.COND_GTE] = True
        if dst < src:
            self.conditions[self.COND_LT] = True
        if dst <= src:
            self.conditions[self.COND_LTE] = True
        if dst == src:
            self.conditions[self.COND_EQ] = True
        if dst != src:
            self.conditions[self.COND_NEQ] = True
        return 0

    def test_i_r(self, src, dst):
        self.init_condition_codes()
        return self.test_all(src, self.registers[dst])

    def test_r_i(self, src, dst):
        self.init_condition_codes()
        return self.test_all(self.registers[src], dst)

    def test_r_r(self, src, dst):
        self.init_condition_codes()
        return self.test_all(self.registers[src], self.registers[dst])

    #
    # JUMPS
    #
    def jump(self, targ):
        self.PC = targ
        return 0

    def jump_notequal(self, targ):
        if self.conditions[self.COND_NEQ] is True:
            self.PC = targ
        return 0

    def jump_equal(self, targ):
        if self.conditions[self.COND_EQ] is True:
            self.PC = targ
        return 0

    def jump_lessthan(self, targ):
        if self.conditions[self.COND_LT] is True:
            self.PC = targ
        return 0

    def jump_lessthanorequal(self, targ):
        if self.conditions[self.COND_LTE] is True:
            self.PC = targ
        return 0

    def jump_greaterthan(self, targ):
        if self.conditions[self.COND_GT] is True:
            self.PC = targ
        return 0

    def jump_greaterthanorequal(self, targ):
        if self.conditions[self.COND_GTE] is True:
            self.PC = targ
        return 0

    #
    # CALL and RETURN
    #
    def call(self, targ):
        self.registers[self.REG_SP] -= 4
        self.memory[self.registers[self.REG_SP]] = self.PC
        self.PC = targ

    def ret(self):
        self.PC = self.memory[self.registers[self.REG_SP]]
        self.registers[self.REG_SP] += 4

    #
    # STACK and related
    #
    def push_r(self, reg):
        self.registers[self.REG_SP] -= 4
        self.memory[self.registers[self.REG_SP]] = self.registers[reg]
        return 0

    def push_m(self, value, reg1, reg2):
        # print 'push_m', value, reg1, reg2
        self.registers[self.REG_SP] -= 4
        tmp = value + self.registers[reg1] + self.registers[reg2]
        # push address onto stack, not memory value itself
        self.memory[self.registers[self.REG_SP]] = tmp
        return 0

    def pop(self):
        self.registers[self.REG_SP] += 4

    def pop_r(self, dst):
        self.registers[dst] = self.registers[self.REG_SP]
        self.registers[self.REG_SP] += 4

    #
    # HELPER func for getarg
    #
    def register_translate(self, r):
        if r in self.reg_name_map:
            return self.reg_name_map[r]
        zassert(False, f'寄存器 {r} 不合法')
        return

    #
    # HELPER in parsing mov (quite primitive) and other ops
    # returns: (value, type)
    # where type is (TYPE_REGISTER, TYPE_IMMEDIATE, TYPE_MEMORY)
    #
    # FORMATS
    #    %ax           - register
    #    $10           - immediate
    #    10            - direct memory
    #    10(%ax)       - memory + reg indirect
    #    10(%ax,%bx)   - memory + 2 reg indirect
    #    10(%ax,%bx,4) - XXX (not handled)
    #
    def getarg(self, arg):
        tmp1 = arg.replace(',', '')
        tmp = tmp1.replace(' \t', '')

        if tmp[0] == '$':
            zassert(len(tmp) == 2, f'正确的形式是 $number (not {tmp})')
            value = tmp.split('$')[1]
            zassert(value.isdigit(), f'[{value}] 必须是一个数字')
            return int(value), 'TYPE_IMMEDIATE'
        elif tmp[0] == '%':
            register = tmp.split('%')[1]
            return self.register_translate(register), 'TYPE_REGISTER'
        elif tmp[0] == '(':
            register = tmp.split('(')[1].split(')')[0].split('%')[1]
            return '%d,%d,%d' % (0, self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
        elif tmp[0] == '.':
            targ = tmp
            return targ, 'TYPE_LABEL'
        elif tmp[0].isalpha() and not tmp[0].isdigit():
            zassert(tmp in self.vars, f'变量 {tmp} 未声明')
            # print '%d,%d,%d' % (self.vars[tmp], self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
            return '%d,%d,%d' % (
                self.vars[tmp], self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
        elif tmp[0].isdigit() or tmp[0] == '-':
            # MOST GENERAL CASE: number(reg,reg) or number(reg)
            # we ignore the common x86 number(reg,reg,constant) for now
            neg = 1
            if tmp[0] == '-':
                tmp = tmp[1:]
                neg = -1
            s = tmp.split('(')
            if len(s) == 1:
                value = neg * int(tmp)
                # print '%d,%d,%d' % (int(value), self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
                return '%d,%d,%d' % (
                    int(value), self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
            elif len(s) == 2:
                value = neg * int(s[0])
                t = s[1].split(')')[0].split(',')
                if len(t) == 1:
                    register = t[0].split('%')[1]
                    # print '%d,%d,%d' % (int(value), self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
                    return '%d,%d,%d' % (
                        int(value), self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
                elif len(t) == 2:
                    register1 = t[0].split('%')[1]
                    register2 = t[1].split('%')[1]
                    # print '%d,%d,%d' % (int(value), self.register_translate(register1), self.register_translate(register2)), 'TYPE_MEMORY'
                    return '%d,%d,%d' % (
                        int(value), self.register_translate(register1),
                        self.register_translate(register2)), 'TYPE_MEMORY'
            else:
                zassert(True, f'mov: 错误的参数 [{arg}]')
                return
        zassert(True, f'mov: 错误的参数 [{arg}]')  # 为啥是True？
        return

    #
    # LOAD a program into memory
    # make it ready to execute
    #
    def load(self, infile, loadaddr):
        pc = int(loadaddr)
        fd = open(infile)

        bpc = loadaddr
        data = 100

        for line in fd:
            cline = line.rstrip()
            # print 'PASS 1', cline

            # remove everything after the comment marker
            ctmp = cline.split('#')
            assert (len(ctmp) == 1 or len(ctmp) == 2)
            if len(ctmp) == 2:
                cline = ctmp[0]

            # remove empty lines, and split line by spaces
            tmp = cline.split()
            if len(tmp) == 0:
                continue

            # only pay attention to labels and variables
            if tmp[0] == '.var':
                assert (len(tmp) == 2)
                assert (tmp[0] not in self.vars)
                self.vars[tmp[1]] = data
                data += 4
                zassert(data < bpc, 'Load address overrun by static data')
                if self.verbose: print('ASSIGN VAR', tmp[0], "-->", tmp[1], self.vars[tmp[1]])
            elif tmp[0][0] == '.':
                assert (len(tmp) == 1)
                self.labels[tmp[0]] = int(pc)
                if self.verbose: print('ASSIGN LABEL', tmp[0], "-->", pc)
            else:
                pc += 1
        fd.close()

        if self.verbose: print('')

        # second pass: do everything else
        pc = int(loadaddr)
        fd = open(infile)
        for line in fd:
            cline = line.rstrip()
            # print 'PASS 2', cline

            # remove everything after the comment marker
            ctmp = cline.split('#')
            assert (len(ctmp) == 1 or len(ctmp) == 2)
            if len(ctmp) == 2:
                cline = ctmp[0]

            # remove empty lines, and split line by spaces
            tmp = cline.split()
            if len(tmp) == 0:
                continue

            # skip labels: all else must be instructions
            if cline[0] != '.':
                tmp = cline.split(None, 1)
                opcode = tmp[0]
                self.pmemory[pc] = cline.strip()

                # MAIN OPCODE LOOP
                if opcode == 'mov':
                    rtmp = tmp[1].split(',', 1)
                    zassert(len(tmp) == 2 and len(rtmp) == 2, 'mov: needs two args, separated by commas [%s]' % cline)
                    arg1 = rtmp[0].strip()
                    arg2 = rtmp[1].strip()
                    (src, stype) = self.getarg(arg1)
                    (dst, dtype) = self.getarg(arg2)
                    # print 'MOV', src, stype, dst, dtype
                    if stype == 'TYPE_MEMORY' and dtype == 'TYPE_MEMORY':
                        print('bad mov: two memory arguments')
                        exit(1)
                    elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_IMMEDIATE':
                        print('bad mov: two immediate arguments')
                        exit(1)
                    elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
                        self.memory[pc] = 'self.move_i_to_r(%d, %d)' % (int(src), dst)
                    elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
                        self.memory[pc] = 'self.move_i_to_r(%d, %d)' % (int(src), dst)
                    elif stype == 'TYPE_MEMORY' and dtype == 'TYPE_REGISTER':
                        tmp = src.split(',')
                        assert (len(tmp) == 3)
                        self.memory[pc] = 'self.move_m_to_r(%d, %d, %d, %d)' % (
                            int(tmp[0]), int(tmp[1]), int(tmp[2]), dst)
                    elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
                        tmp = dst.split(',')
                        assert (len(tmp) == 3)
                        self.memory[pc] = 'self.move_r_to_m(%d, %d, %d, %d)' % (
                            src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
                    elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
                        self.memory[pc] = 'self.move_r_to_r(%d, %d)' % (src, dst)
                    elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_MEMORY':
                        tmp = dst.split(',')
                        assert (len(tmp) == 3)
                        self.memory[pc] = 'self.move_i_to_m(%d, %d, %d, %d)' % (
                            src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
                    else:
                        zassert(False, 'malformed mov instruction')
                elif opcode == 'pop':
                    if len(tmp) == 1:
                        self.memory[pc] = 'self.pop()'
                    elif len(tmp) == 2:
                        arg = tmp[1].strip()
                        (dst, dtype) = self.getarg(arg)
                        zassert(dtype == 'TYPE_REGISTER', 'Can only pop into a register')
                        self.memory[pc] = 'self.pop_r(%d)' % dst
                    else:
                        zassert(False, 'pop instruction must take zero/one args')
                elif opcode == 'push':
                    (src, stype) = self.getarg(tmp[1].strip())
                    if stype == 'TYPE_REGISTER':
                        self.memory[pc] = 'self.push_r(%d)' % (int(src))
                    elif stype == 'TYPE_MEMORY':
                        tmp = src.split(',')
                        assert (len(tmp) == 3)
                        self.memory[pc] = 'self.push_m(%d,%d,%d)' % (int(tmp[0]), int(tmp[1]), int(tmp[2]))
                    else:
                        zassert(False, 'Cannot push anything but registers')
                elif opcode == 'call':
                    (targ, ttype) = self.getarg(tmp[1].strip())
                    if ttype == 'TYPE_LABEL':
                        self.memory[pc] = 'self.call(%d)' % (int(self.labels[targ]))
                    else:
                        zassert(False, 'Cannot call anything but a label')
                elif opcode == 'ret':
                    assert (len(tmp) == 1)
                    self.memory[pc] = 'self.ret()'
                elif opcode == 'add':
                    rtmp = tmp[1].split(',', 1)
                    zassert(len(tmp) == 2 and len(rtmp) == 2, 'add: needs two args, separated by commas [%s]' % cline)
                    arg1 = rtmp[0].strip()
                    arg2 = rtmp[1].strip()
                    (src, stype) = self.getarg(arg1)
                    (dst, dtype) = self.getarg(arg2)
                    if stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
                        self.memory[pc] = 'self.add_i_to_r(%d, %d)' % (int(src), dst)
                    elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
                        self.memory[pc] = 'self.add_r_to_r(%d, %d)' % (int(src), dst)
                    else:
                        zassert(False, 'malformed usage of add instruction')
                elif opcode == 'sub':
                    rtmp = tmp[1].split(',', 1)
                    zassert(len(tmp) == 2 and len(rtmp) == 2, 'sub: needs two args, separated by commas [%s]' % cline)
                    arg1 = rtmp[0].strip()
                    arg2 = rtmp[1].strip()
                    (src, stype) = self.getarg(arg1)
                    (dst, dtype) = self.getarg(arg2)
                    if stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
                        self.memory[pc] = 'self.sub_i_to_r(%d, %d)' % (int(src), dst)
                    elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
                        self.memory[pc] = 'self.sub_r_to_r(%d, %d)' % (int(src), dst)
                    else:
                        zassert(False, 'malformed usage of sub instruction')
                elif opcode == 'fetchadd':
                    rtmp = tmp[1].split(',', 1)
                    zassert(len(tmp) == 2 and len(rtmp) == 2,
                            'fetchadd: needs two args, separated by commas [%s]' % cline)
                    arg1 = rtmp[0].strip()
                    arg2 = rtmp[1].strip()
                    (src, stype) = self.getarg(arg1)
                    (dst, dtype) = self.getarg(arg2)
                    tmp = dst.split(',')
                    assert (len(tmp) == 3)
                    if stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
                        self.memory[pc] = 'self.fetchadd(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
                    else:
                        zassert(False, 'poorly specified fetch and add')
                elif opcode == 'xchg':
                    rtmp = tmp[1].split(',', 1)
                    zassert(len(tmp) == 2 and len(rtmp) == 2, 'xchg: needs two args, separated by commas [%s]' % cline)
                    arg1 = rtmp[0].strip()
                    arg2 = rtmp[1].strip()
                    (src, stype) = self.getarg(arg1)
                    (dst, dtype) = self.getarg(arg2)
                    tmp = dst.split(',')
                    assert (len(tmp) == 3)
                    if stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
                        self.memory[pc] = 'self.atomic_exchange(%d, %d, %d, %d)' % (
                            src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
                    else:
                        zassert(False, 'poorly specified atomic exchange')
                elif opcode == 'test':
                    rtmp = tmp[1].split(',', 1)
                    zassert(len(tmp) == 2 and len(rtmp) == 2, 'test: needs two args, separated by commas [%s]' % cline)
                    arg1 = rtmp[0].strip()
                    arg2 = rtmp[1].strip()
                    (src, stype) = self.getarg(arg1)
                    (dst, dtype) = self.getarg(arg2)
                    if stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
                        self.memory[pc] = 'self.test_i_r(%d, %d)' % (int(src), dst)
                    elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
                        self.memory[pc] = 'self.test_r_r(%d, %d)' % (int(src), dst)
                    elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_IMMEDIATE':
                        self.memory[pc] = 'self.test_r_i(%d, %d)' % (int(src), dst)
                    else:
                        zassert(False, 'malformed usage of test instruction')
                elif opcode == 'j':
                    (targ, ttype) = self.getarg(tmp[1].strip())
                    zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
                    self.memory[pc] = 'self.jump(%d)' % int(self.labels[targ])
                elif opcode == 'jne':
                    (targ, ttype) = self.getarg(tmp[1].strip())
                    zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
                    self.memory[pc] = 'self.jump_notequal(%d)' % int(self.labels[targ])
                elif opcode == 'je':
                    (targ, ttype) = self.getarg(tmp[1].strip())
                    zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
                    self.memory[pc] = 'self.jump_equal(%d)' % self.labels[targ]
                elif opcode == 'jlt':
                    (targ, ttype) = self.getarg(tmp[1].strip())
                    zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
                    self.memory[pc] = 'self.jump_lessthan(%d)' % int(self.labels[targ])
                elif opcode == 'jlte':
                    (targ, ttype) = self.getarg(tmp[1].strip())
                    zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
                    self.memory[pc] = 'self.jump_lessthanorequal(%s)' % self.labels[targ]
                elif opcode == 'jgt':
                    (targ, ttype) = self.getarg(tmp[1].strip())
                    zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
                    self.memory[pc] = 'self.jump_greaterthan(%d)' % int(self.labels[targ])
                elif opcode == 'jgte':
                    (targ, ttype) = self.getarg(tmp[1].strip())
                    zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
                    self.memory[pc] = 'self.jump_greaterthanorequal(%s)' % self.labels[targ]
                elif opcode == 'nop':
                    self.memory[pc] = 'self.nop()'
                elif opcode == 'halt':
                    self.memory[pc] = 'self.halt()'
                elif opcode == 'yield':
                    self.memory[pc] = 'self.iyield()'
                elif opcode == 'rdump':
                    self.memory[pc] = 'self.rdump()'
                elif opcode == 'mdump':
                    self.memory[pc] = 'self.mdump(%s)' % tmp[1]
                else:
                    print('illegal opcode: ', opcode)
                    exit(1)

                if self.verbose: print('pc:%d LOADING %20s --> %s' % (pc, self.pmemory[pc], self.memory[pc]))

                # INCREMENT PC for loader
                pc += 1
        # END: loop over file
        fd.close()
        if self.verbose: print('')
        return

    # END: load

    def print_headers(self, procs):
        # print some headers
        if len(self.memtrace) > 0:
            for m in self.memtrace:
                if m[0].isdigit():
                    print('%5d' % int(m), end=' ')
                else:
                    zassert(m in self.vars, 'Traced variable %s not declared' % m)
                    print('%5s' % m, end=' ')
            print(' ', end=' ')
        if len(self.regtrace) > 0:
            for r in self.regtrace:
                print('%5s' % self.get_regname(r), end=' ')
            print(' ', end=' ')
        if self.cctrace is True:
            print('>= >  <= <  != ==', end=' ')

            # and per x86ThreadSim
        for i in range(procs.getnum()):
            print('       Thread %d        ' % i, end=' ')
        print('')
        return

    def print_trace(self, newline):
        if len(self.memtrace) > 0:
            for m in self.memtrace:
                if self.compute:
                    if m[0].isdigit():
                        print('%5d' % self.memory[int(m)], end=' ')
                    else:
                        zassert(m in self.vars, 'Traced variable %s not declared' % m)
                        print('%5d' % self.memory[self.vars[m]], end=' ')
                else:
                    print('%5s' % '?', end=' ')
            print(' ', end=' ')
        if len(self.regtrace) > 0:
            for r in self.regtrace:
                if self.compute:
                    print('%5d' % self.registers[r], end=' ')
                else:
                    print('%5s' % '?', end=' ')
            print(' ', end=' ')
        if self.cctrace is True:
            for c in self.cond_list:
                if self.compute:
                    if self.conditions[c]:
                        print('1 ', end=' ')
                    else:
                        print('0 ', end=' ')
                else:
                    print('? ', end=' ')
        if (len(self.memtrace) > 0 or len(self.regtrace) > 0 or self.cctrace is True) and newline == True:
            print('')
        return

    def setint(self, intfreq, intrand):
        if intrand == False:
            return intfreq
        return int(random.random() * intfreq) + 1

    def run(self, procs, intfreq, intrand):
        # hw init: cc's, interrupt frequency, etc.
        interrupt = self.setint(intfreq, intrand)
        icount = 0

        self.print_headers(procs)
        self.print_trace(True)

        while True:
            # need x86ThreadSim ID of current ProcSims
            tid = procs.getcurr().gettid()

            # FETCH
            prevPC = self.PC
            instruction = self.memory[self.PC]
            self.PC += 1

            # DECODE and EXECUTE
            # key: self.PC may be changed during eval; thus MUST be incremented BEFORE eval
            rc = eval(instruction)

            # tracing details: ALWAYS AFTER EXECUTION OF INSTRUCTION
            self.print_trace(False)

            # output: x86ThreadSim-proportional spacing followed by PC and instruction
            dospace(tid)
            print(prevPC, self.pmemory[prevPC])
            icount += 1

            # halt instruction issued
            if rc == -1:
                procs.done()
                if procs.numdone() == procs.getnum():
                    return icount
                next(procs)
                procs.restore()

                self.print_trace(False)
                for i in range(procs.getnum()):
                    print('----- Halt;Switch ----- ', end=' ')
                print('')

            # do interrupt processing
            interrupt -= 1
            if interrupt == 0 or rc == -2:
                interrupt = self.setint(intfreq, intrand)
                procs.save()
                next(procs)
                procs.restore()

                self.print_trace(False)
                for i in range(procs.getnum()):
                    print('------ Interrupt ------ ', end=' ')
                print('')
        # END: while
        return


#
# END: class cpu
#


#
# PROCESS LIST class
#
class proclist:
    def __init__(self):
        self.plist = []
        self.curr = 0
        self.active = 0

    def done(self):
        self.plist[self.curr].setdone()
        self.active -= 1

    def numdone(self):
        return len(self.plist) - self.active

    def getnum(self):
        return len(self.plist)

    def add(self, p):
        self.active += 1
        self.plist.append(p)

    def getcurr(self):
        return self.plist[self.curr]

    def save(self):
        self.plist[self.curr].save()

    def restore(self):
        """执行当前指令"""
        self.plist[self.curr].restore()

    def __next__(self):
        for i in range(self.curr + 1, len(self.plist)):
            if self.plist[i].isdone() is False:
                self.curr = i
                return
        for i in range(0, self.curr + 1):
            if self.plist[i].isdone() is False:
                self.curr = i
                return


#
# PROCESS class
#
class process:
    def __init__(self, cpu, tid, pc, stackbottom, reginit):
        self.cpu = cpu  # object reference
        self.tid = tid
        self.pc = pc
        self.regs = {}  # 每个进程都有自己单独的寄存器组
        self.cc = {}
        self.done = False
        self.stack = stackbottom

        # init regs: all 0 or specially set to something
        for r in self.cpu.get_reglist():
            self.regs[r] = 0
        if reginit != '':
            # form: ax=1,bx=2 (for some subset of registers)
            for r in reginit.split(':'):  # 用冒号来区分同一个进程的多个不同的初始化参数!
                tmp = r.split('=')
                assert (len(tmp) == 2)
                self.regs[self.cpu.get_regnum(tmp[0])] = int(tmp[1])

        # init CCs
        for c in self.cpu.get_condlist():
            self.cc[c] = False  # 每个进程也有自己的PSW

        # stack
        self.regs[self.cpu.get_regnum('sp')] = stackbottom
        # print 'REG', self.cpu.get_regnum('sp'), self.regs[self.cpu.get_regnum('sp')]
        return

    def gettid(self):
        return self.tid

    def save(self):
        self.pc = self.cpu.get_pc()
        for c in self.cpu.get_condlist():
            self.cc[c] = self.cpu.get_cond(c)
        for r in self.cpu.get_reglist():
            self.regs[r] = self.cpu.get_reg(r)

    def restore(self):
        self.cpu.set_pc(self.pc)
        for c in self.cpu.get_condlist():
            self.cpu.set_cond(c, self.cc[c])
        for r in self.cpu.get_reglist():
            self.cpu.set_reg(r, self.regs[r])

    def setdone(self):
        self.done = True

    def isdone(self):
        return self.done == True


class Core:
    def __init__(self, seed=0, threads=2, program='looping-race-nolock.s', interrupt=4, randints=False, argv='ax=1',
                 loadaddr=1000, memsize=128, memtrace='2000', regtrace='bx,ax', cctrace=False, printstats=True):
        parser = OptionParser()
        parser.add_option('-s', '--seed', default=seed, help='the random seed', action='store', type='int', dest='seed')
        parser.add_option('-t', '--threads', default=threads, help='number of threads', action='store', type='int',
                          dest='numthreads')
        parser.add_option('-p', '--program', default=program, help='source program (in .s)',
                          action='store', type='string',
                          dest='progfile')
        parser.add_option('-i', '--interrupt', default=interrupt, help='interrupt frequency', action='store', type='int',
                          dest='intfreq')
        parser.add_option('-r', '--randints', default=randints, help='if interrupts are random', action='store_true',
                          dest='intrand')
        parser.add_option('-a', '--argv', default=argv,
                          help='comma-separated per-x86ThreadSim args (e.g., ax=1,ax=2 sets x86ThreadSim 0 ax reg to 1 and x86ThreadSim 1 ax reg to 2); specify multiple regs per x86ThreadSim via colon-separated list (e.g., ax=1:bx=2,cx=3 sets x86ThreadSim 0 ax and bx and just cx for x86ThreadSim 1)',
                          action='store', type='string', dest='argv')
        parser.add_option('-L', '--loadaddr', default=loadaddr, help='address where to load code', action='store',
                          type='int',
                          dest='loadaddr')
        parser.add_option('-m', '--memsize', default=memsize, help='size of address space (KB)', action='store', type='int',
                          dest='memsize')
        parser.add_option('-M', '--memtrace', default=memtrace,
                          help='comma-separated list of addrs to trace (e.g., 20000,20001)',
                          action='store',
                          type='string', dest='memtrace')
        parser.add_option('-R', '--regtrace', default=regtrace,
                          help='comma-separated list of regs to trace (e.g., ax,bx,cx,dx)',
                          action='store',
                          type='string', dest='regtrace')
        parser.add_option('-C', '--cctrace', default=cctrace, help='should we trace condition codes', action='store_true',
                          dest='cctrace')
        parser.add_option('-S', '--printstats', default=printstats, help='print some extra stats', action='store_true',
                          dest='printstats')
        parser.add_option('-v', '--verbose', default=False, help='print some extra info', action='store_true',
                          dest='verbose')  # 应该是调试的时候用的
        parser.add_option('-c', '--compute', default=True, help='compute answers for me', action='store_true',
                          dest='solve')
        (self.options, self.args) = parser.parse_args()
        self.deal_args()

    def show_args(self):
        options = self.options
        print('随机种子', options.seed)
        print('线程数', options.numthreads)
        print('程序', options.progfile.split('/')[-1])
        print('中断频率', options.intfreq)
        print('中断随机', options.intrand)
        print('初始化参数', options.argv)
        print('代码地址', options.loadaddr)
        print('内存大小', options.memsize)
        print('内存追踪', options.memtrace)
        print('寄存器追踪', options.regtrace)
        print('PSW追踪', options.cctrace)
        print('状态信息', options.printstats)
        # print('verbose', options.verbose)

    def deal_args(self):
        options = self.options
        seed = int(options.seed)
        random.seed(seed)

        num_threads = int(options.numthreads)
        int_freq = int(options.intfreq)

        zassert(int_freq > 0, f'Interrupt frequency must be greater than 0 not {int_freq}')
        int_rand = int(options.intrand)
        prog_file = options.progfile
        zassert(prog_file != '', 'Program file must be specified')

        argv = options.argv.split(',')  # 初始化参数 例如 'bx=1, dx=2'
        zassert(len(argv) == num_threads or len(argv) == 1,
                'argv: must be one per-x86ThreadSim or just one set of values for all threads')
        load_addr = options.loadaddr  # 指定代码存放位置
        mem_size = options.memsize  # 总内存大小

        memtrace = []  # 记录需要追踪的内存地址
        if options.memtrace != '':
            for m in options.memtrace.split(','):
                memtrace.append(m)

        regtrace = []  # 记录需要追踪的寄存器
        if options.regtrace != '':
            for r in options.regtrace.split(','):
                regtrace.append(r)

        cctrace = options.cctrace  # 是否追踪PSW

        printstats = options.printstats  # 是否打印一些额外的信息
        verbose = options.verbose

        # debug = False  # ???

        cpu = CPU(mem_size, memtrace, regtrace, cctrace, options.solve, verbose)

        # load a program
        cpu.load(prog_file, load_addr)

        # ProcSims list
        procs = proclist()
        pid = 0
        stack = mem_size * 1000  # mem_size单位是KB,但是为啥这里要乘以1000
        for t in range(num_threads):
            if len(argv) > 1:
                arg = argv[pid]  # 这里是因为可以给不同的线程添加不同的初始化条件
            else:
                arg = argv[0]  # 如果只有一个初始化参数, 就将这个参数应用到所有线程上
            procs.add(process(cpu, pid, load_addr, stack, arg))
            stack -= 1000  # 每个线程分配1000B的栈区域
            pid += 1

        # get first one ready!
        procs.restore()
        # run it
        t1 = time.perf_counter()
        ic = cpu.run(procs, int_freq, int_rand)
        t2 = time.perf_counter()

        if printstats:
            print(f'STATS:: Instructions    {ic}')
            print(f'STATS:: Emulation Rate  %.2f kinst/sec' % (float(ic) / float(t2 - t1) / 1000.0))

        # use this for profiling
        # import cProfile
        # cProfile.run('run()')


def main():
    core = Core()
    core.show_args()


if __name__ == "__main__":
    main()
